﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace PDChart
{
    public partial class PRPSChart3D : PDChart
    {
        public PRPSChart3D()
        {
            InitializeComponent();
        }

        public override bool LoadFile(string filename)
        {
            PDFile pdf = new PDFile();

            try
            {
                pdf.Load(filename, 12);

                Set(pdf.ToPRPS(100));

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }


        protected override void OnDraw(Graphics g)
        {

            float w = (float)this.ClientRectangle.Width;
            float h = (float)this.ClientRectangle.Height;
            
            DrawText(g);

            PaletteLegend pal = new PaletteLegend(new PointF(w * 0.02f, h * 0.15f), new SizeF(w * 0.03f, h * 0.4f), this.LabelFont, "0%", (100 / GetCurrentAmp()).ToString() + "%");

            pal.Draw(g);


            if (w / h < 1.3)
            {
                h = w / 1.3f;
            }
            else
            {
                w = h * 1.3f;
            }


            MaxValue = new XYZ(50, 200, 1);

            Grid3D grid = new Grid3D(new PointF(w * 0.2f, h * 0.58f),
                                     new XYZ(w * 0.3f, h * 0.6f, h * 0.25f),
                                     this.LabelFont,
                                     0.8f,
                                     60 / 180.0 * Math.PI);

            grid.DrawBackground(g);

            float barwidth = (grid.GetPosition(0, 1).Y - grid.Location.Y) / MaxValue.Y + 0.5f;

            for (int i = 0; i < Data.GetLength(0); i++)
            {
                for (int j = 0; j < Data.GetLength(1); j++)
                {
                    if (Data[i, j] == 0)
                        continue;

                    Color color = pal.GetColor(Data[i, j] * GetCurrentAmp());

                    XYZ xyz = grid.GetLocationHeight(j / MaxValue.Y, i / MaxValue.X, (float)Data[i, j]);
                    
                    using (Pen pen = new Pen(color, barwidth))
                    {
                        g.DrawLine(pen, new PointF(xyz.X + barwidth, xyz.Y), new PointF(xyz.X + barwidth, xyz.Y - xyz.Z));
                    }               
                }
            }
            

            base.OnDraw(g);
        }

    }


    public class Grid3D
    {
        public float ShearWarpFactor = 0.8f;
        public double RotateAngle;

        public Color Color1 = Color.DarkGray;
        public Color Color2 = Color.Silver;
        public Color Color3 = Color.Silver;
        public Color LineColor = Color.White;

        public Font Font;
        public PointF Location;
        protected XYZ Size;
        
        public Grid3D(PointF location, XYZ size, Font font, float shearWarpFactor, double rotateAngle)
        {
            Location = location;
            Size = size;
            Font = font;
            ShearWarpFactor = shearWarpFactor;
            RotateAngle = rotateAngle;
        }


        public XYZ GetLocationHeight(float x, float y, float z)
        {
            if (z <= 0)
                z = 0;

            if (z >= 1)
            {
                z = 1;
            }

            float height = z * Size.Z;
            
            PointF xy = GetPosition(x, y);

            return new XYZ(xy.X, xy.Y, height);
        }

        public PointF GetPosition(float x, float y)
        {
            float y0 = (float)(y * Size.Y);
            float x0 = (float)(x * Size.X);

            float y1 = y0 + ShearWarpFactor * x0;
            float x1 = x0;

            return new PointF(Location.X + (float)(x1 * Math.Cos(RotateAngle) + y1 * Math.Sin(RotateAngle)),
                                Location.Y + (float)(x1 * -Math.Sin(RotateAngle) + y1 * Math.Cos(RotateAngle)));

        }

        public PointF GetPosition(PointF p)
        {
            return GetPosition(p.X, p.Y);
        }


        public void DrawBackground(Graphics g)
        {
            using (SolidBrush sb = new SolidBrush(Color3))
            {

                g.FillPolygon(sb, new PointF[] {
                            Location, 
                            Location - new SizeF(0, Size.Z),                            
                            GetPosition(1, 0) - new SizeF(0, Size.Z), 
                            GetPosition(1, 0)});

            }

            int nx = 5;
            int ny = 4;
            int nz = 2;

            using (SolidBrush sb1 = new SolidBrush(Color1))
            using (SolidBrush sb2 = new SolidBrush(Color2))
            {
                SolidBrush sb0;

                for (int i = 0; i < ny; i++)
                {
                    for (int j = 0; j < nx; j++)
                    {
                        if ((i + j) % 2 == 0)
                            sb0 = sb1;
                        else
                            sb0 = sb2;


                        g.FillPolygon(sb0, new PointF[] {
                            GetPosition((float)i / ny, (float)j / nx),
                            GetPosition((float)(i + 1) / ny, (float)j / nx), 
                             GetPosition((float)(i + 1) / ny, (float)(j + 1) / nx),
                            GetPosition((float)i / ny, (float)(j + 1) / nx),
                           
                            });

                    }
                }

            }
           
            using (Pen pen = new Pen(new SolidBrush(Color.White)))
            {

                for (int i = 0; i <= nx; i++)
                {
                    g.DrawLine(pen, GetPosition(0, (float)i / nx), GetPosition(1f, (float)i / nx));
                }

                for (int i = 0; i <= ny; i++)
                {
                    g.DrawLine(pen, GetPosition((float)i / ny, 0), GetPosition((float)i / ny, 1f));
                    PointF pf = GetPosition((float)i / ny, 0);
                    g.DrawLine(pen, pf, pf - new SizeF(0, Size.Z));
                }

                for (int i = 1; i <= nz; i++)
                {
                    PointF pf = GetPosition(1, 0);
                    SizeF sf = new SizeF(0, Size.Z / nz * i);
                    g.DrawLine(pen, Location - sf, pf - sf);
                }

            }

            using (SolidBrush brush = new SolidBrush(Color.White))
            {
                g.DrawString(" 0", this.Font, brush, GetPosition(-5f / 72, -3f / 50));
                g.DrawString("10", this.Font, brush, GetPosition(-5f / 72, 7f / 50));
                g.DrawString("20", this.Font, brush, GetPosition(-5f / 72, 17f / 50));
                g.DrawString("30", this.Font, brush, GetPosition(-5f / 72, 27f / 50));
                g.DrawString("40", this.Font, brush, GetPosition(-5f / 72, 37f / 50));
                g.DrawString("50", this.Font, brush, GetPosition(-5f / 72, 47f / 50));
                g.DrawString("0", this.Font, brush, GetPosition(-4f / 72, 53f / 50));
                g.DrawString("+90", this.Font, brush, GetPosition((18 - 4f) / 72, 53f / 50));
                g.DrawString("0", this.Font, brush, GetPosition((36 - 4f) / 72, 53f / 50));
                g.DrawString("-90", this.Font, brush, GetPosition((54 - 4f) / 72, 53f / 50));
                g.DrawString("0", this.Font, brush, GetPosition((72 - 4f) / 72, 53f / 50));
            }
        }



    }
}
